<template>
    <view>
        <uni-popup
            ref="popupRef"
            type="bottom"
            :animation="animation"
            :is-mask-click="isMaskClick"
            :mask-background-color="maskBackgroundColor"
            :background-color="backgroundColor"
            :safe-area="safeArea"
            @change="change"
            @maskClick="maskClick"
            @touchmove.stop.prevent
        >
            <view
                ref="touchBox"
                class="touch-box"
                :style="{
                    height: contentHeight,
                    transform: `translateY(${dy}px)`
                }"
                @mousedown="touchstart"
                @touchstart="touchstart"
                @touchmove="touchmove"
                @touchend="touchend"
            >
                <view class="touch-bar">
                    <view :class="['notice-bar', { active: isMoving }]"></view>
                </view>
                <view class="content">
                    <slot></slot>
                </view>
            </view>
        </uni-popup>
    </view>
</template>

<script lang="ts" setup>
import { ref, nextTick } from "vue";
const emits = defineEmits(["touchstart", "touchmove", "touchend", "change", "maskClick"]);
const props = defineProps({
    contentHeight: {
        type: String,
        default: "auto"
    },
    threshold: {
        type: [Number],
        default: 220
    },
    speed: {
        type: Number,
        default: 0.5
    },
    animation: {
        type: Boolean,
        default: true
    },
    isMaskClick: {
        type: Boolean,
        default: true
    },
    maskBackgroundColor: {
        type: String,
        default: "rgba(0,0,0,0.4)"
    },
    backgroundColor: {
        type: String,
        default: "none"
    },
    safeArea: {
        type: Boolean,
        default: true
    },
    beforeClose: {
        type: Function,
        default: null
    }
});

const popupRef: any = ref(null);
const startPageY = ref(0);
const startTime = ref(0);
const dy = ref(0);
const isMoving = ref<boolean>(false);
const isClosed = ref<boolean>(false);

// 打开
const open = () => {
    // 初始化状态
    dy.value = 0;
    isMoving.value = false;
    isClosed.value = false;
    nextTick(() => {
        popupRef.value.open();
    });
};
// 关闭
const close = () => {
    if (isClosed.value) return;
    isClosed.value = true;
    const fn = () => {
        popupRef.value.close();
    };
    if (props.beforeClose) {
        dy.value = 0;
        props.beforeClose(fn);
    } else {
        fn();
    }
};
// 按下
const touchstart = (e: any) => {
    startTime.value = Date.now();
    startPageY.value = e.changedTouches[0]?.pageY || 0;
    emits("touchstart", e);
};
// 滑动
const touchmove = (e: any) => {
    const pageY: number = e.changedTouches[0]?.pageY || 0;
    dy.value = pageY - startPageY.value > 0 ? pageY - startPageY.value : 0;

    // 向下滑动改变指示箭头
    if (dy.value > 0 && !isMoving.value) {
        isMoving.value = true;
    } else if (dy.value <= 0 && isMoving.value) {
        isMoving.value = false;
    }

    // 滑动超出指定距离关闭弹窗
    if (dy.value >= props.threshold) {
        close();
    }
    emits("touchmove", e);
};

// 松手
const touchend = (e: any) => {
    if (isMoving.value) {
        isMoving.value = false;
    }
    // 距离不够但是滑动速度超过给定速率
    const v = dy.value / (Date.now() - startTime.value);
    if (v >= props.speed) {
        close();
    } else {
        dy.value = 0;
    }
    emits("touchend", e);
};

const change = (e: any) => {
    emits("change", e);
};
const maskClick = () => {
    emits("maskClick");
};
defineExpose({ open, close });
</script>

<style lang="scss" scoped>
.touch-box {
    background-color: #fff;
    border-top-left-radius: 24rpx;
    border-top-right-radius: 24rpx;
    display: flex;
    flex-direction: column;
    overflow: hidden;

    .touch-bar {
        width: 100%;
        height: 30px;
        display: flex;
        justify-content: center;
        align-items: center;

        .notice-bar {
            width: 50px;
            height: 8px;
            position: relative;

            &::before,
            &::after {
                content: "";
                position: absolute;
                top: 0;
                width: 50%;
                height: 8px;
                background-color: #e3e3e3;
                border-radius: 10px;
                transition: 0.1s ease;
            }

            &::before {
                left: 2.5px;
                transform-origin: right center;
            }

            &::after {
                right: 2.5px;
                transform-origin: left center;
            }

            &.active::before {
                transform: rotate(15deg);
            }

            &.active::after {
                transform: rotate(-15deg);
            }
        }
    }

    .content {
        flex: 1;
        position: relative;
    }
}
</style>
